import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource;
import java.util.ArrayList;
import java.util.HashMap;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;


public class Application extends JFrame {
	
	static Application instance = null;
	private HashMap<Integer, Image> morceaux;
	private HashMap<Integer, JButton> boutons;
	private Integer dimension = 2;
	private Integer posTrou = dimension*dimension;
	
	private int[] sequenceRepresentee;
	private boolean flagMelange = false;

	private JComboBox algos;
	private JButton melanger;
	
	private Melangeur melangeur;
	
	// Acces à l'instance de l'Administrateur
	public static Application getInstance(){
		if(instance == null){
			instance = new Application();
		}
		return instance;
	}
	
	// Constructeur de l'Administrateur
	private Application() {
		// TODO Auto-generated constructor stub
		createInstance();
	}
	
	// Création de l'instance
	private void createInstance() {
		setTitle("LSIA");
		setSize(500,580);
		setResizable(false);
		
		morceaux = new HashMap<Integer, Image>();
		boutons = new HashMap<Integer, JButton>();
		sequenceRepresentee = new int[dimension*dimension];
		flagMelange = false;
		
		JPanel panelMain = new JPanel();
		panelMain.setLayout(new GridLayout(1, 0));
		
		getContentPane().add(panelMain);
		
		JPanel panelTaquin = new JPanel();
		panelTaquin.setLayout(new GridLayout(dimension, dimension));
		panelTaquin.setMaximumSize(new Dimension(500, 500));
		panelTaquin.setMinimumSize(new Dimension(500, 500));
		
		
		// Decoupage de l'image
		Toolkit tk = Toolkit.getDefaultToolkit();
		Image img = tk.getImage("img/img.jpg");
		img = img.getScaledInstance(500, 500, Image.SCALE_DEFAULT);
		
		Integer id=1;
		for(Integer i=0;i<dimension;i++){
			for(Integer j=0;j<dimension;j++){
				Image portionImage = tk.createImage (new FilteredImageSource (img.getSource(),new CropImageFilter (j*500/dimension, i*500/dimension, 500/dimension, 500/dimension)));
				morceaux.put(id, portionImage);
				
				JButton jbutton = new JButton(new ImageIcon(portionImage));
				jbutton.addActionListener(new ActionListener() {
					
					@Override
					public void actionPerformed(ActionEvent e) {
						// TODO Auto-generated method stub
						Application.getInstance().echange((JButton)e.getSource());
					}
				});
				panelTaquin.add(jbutton);				
				boutons.put(id, jbutton);
				
				sequenceRepresentee[id-1] = id;
				
				id++;
			}
		}
		
		// On enlève le dernier bouton
		boutons.get(posTrou).setIcon(null);
		boutons.get(posTrou).setEnabled(false);
		
		panelMain.add(panelTaquin);
		panelMain.setLayout(new BoxLayout(panelMain, BoxLayout.Y_AXIS));
		
		JPanel panelControle = new JPanel();
		panelControle.setLayout(new GridLayout(0,4));
		panelControle.add(new JLabel(""));
		panelControle.add(new JLabel(""));
		panelControle.add(new JLabel(""));
		panelControle.add(new JLabel(""));
		
		melanger = new JButton("Mélanger");
		melanger.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				Application.getInstance().basculerBoutonMelanger();
			}
		});
		panelControle.add(melanger);
				
		panelControle.add(new JLabel("Algorithme : ", JLabel.RIGHT));
		
		algos = new JComboBox();
		algos.addItem("Décodage Séquentiel");
		algos.addItem("Meilleur d'abord");
		algos.addItem("A*");
		
		panelControle.add(algos);
		JButton lancer = new JButton("Lancer");
		lancer.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				Application.getInstance().preparerLancementAlgo();
				
			}
		});
		panelControle.add(lancer);
		
		panelMain.add(panelControle);
		
		// Affichage de la fenêtre
		setVisible(true);
		// Comportement a la suppression
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

	}
	
	// Réalise graphiquement un mouvement du taquin
	public void echange(JButton b){
		for(Integer i=1; i<=dimension*dimension; i++){
			if(b == boutons.get(i)){				
				echange(posTrou,i);
			}
		}
	}
	
	public void echange(Integer id1, Integer id2){
		
		if(valide(id1, id2)){
			
			// On echange graphiquement
			boutons.get(id1).setIcon(boutons.get(id2).getIcon());
			boutons.get(id1).setEnabled(true);
			boutons.get(id2).setIcon(null);
			boutons.get(id2).setEnabled(false);
			
			// On echange la sequence
			int tmp = sequenceRepresentee[id1-1];
			sequenceRepresentee[id1-1] = sequenceRepresentee[id2-1];
			sequenceRepresentee[id2-1] = tmp;
			
			posTrou = id2;			
		}
	}

	
	// Retourne vrai si l'échange est possible
	public boolean valide(Integer p) {
		if(posTrou+dimension == p || posTrou-dimension == p || (posTrou+1 == p && p%dimension!=1) || (posTrou-1 == p && p%dimension!=0)){
			return true;
		}
		else{
			return false;
		}
	}
	
	// Retourne vrai si l'échange est possible
	public boolean valide(Integer id1, Integer id2) {
		if(id1+dimension == id2 || id1-dimension == id2 || (id1+1 == id2 && id2%dimension!=1) || (id1-1 == id2 && id2%dimension!=0)){
			return true;
		}
		else{
			return false;
		}
	}
	
	// Retourne la dimension du taquin
	public Integer getDimension(){
		return dimension;
	}
	
	public Integer getTrou(){
		return posTrou;
	}
	
	public void basculerBoutonMelanger(){
		flagMelange = !flagMelange;
		if(flagMelange){
			melanger.setText("Arrêter");
			melangeur = new Melangeur();
			melangeur.start();
		}
		else{
			melanger.setText("Mélanger");
			melangeur.suspendre();
		}		
	}
	
	
	// Préparation du lancement de l'algorithme
	public void preparerLancementAlgo(){
		//System.out.println(sequenceRepresentee.toString());
		Object mode = algos.getSelectedItem();
		if(mode == "Décodage Séquentiel"){
			lancerDecodageSequentiel();
		}else if(mode == "A*"){
			lancerA();
		}else if(mode == "Meilleur d'abord"){
			lancerMeilleurFirst();
		}
	}

	private void lancerMeilleurFirst() {
		// TODO Auto-generated method stub
		MeilleurFirst gsTaquin = new MeilleurFirst(null);
		
		NoeudTaquin debutTaquin = new NoeudTaquin("", sequenceRepresentee);
		debutTaquin.construireNom();
		
		int[] dernier = finTaquin();
		NoeudTaquin dernierNoeud = new NoeudTaquin("fin", dernier);
		dernierNoeud.construireNom();
		
		ArrayList<Noeud> finTaquin = new ArrayList<Noeud>();
		finTaquin.add(dernierNoeud);
		
		Integer resultTaquin = gsTaquin.algo(debutTaquin, finTaquin);
		
		System.out.println(gsTaquin.resultat().toString());

		System.out.println("Result algo : "+resultTaquin.toString());
		Simulateur simuler = new Simulateur(gsTaquin.resultat());
		simuler.start();
	}

	private void lancerA() {
		// TODO Auto-generated method stub
		A gsTaquin = new A(null);
		
		NoeudTaquin debutTaquin = new NoeudTaquin("", sequenceRepresentee);
		debutTaquin.construireNom();
		
		int[] dernier = finTaquin();
		NoeudTaquin dernierNoeud = new NoeudTaquin("fin", dernier);
		dernierNoeud.construireNom();
		
		ArrayList<Noeud> finTaquin = new ArrayList<Noeud>();
		finTaquin.add(dernierNoeud);
		
		Integer resultTaquin = gsTaquin.algo(debutTaquin, finTaquin);
		
		System.out.println(gsTaquin.resultat().toString());

		System.out.println("Result algo : "+resultTaquin.toString());
		Simulateur simuler = new Simulateur(gsTaquin.resultat());
		simuler.start();
		
		
	}

	private void lancerDecodageSequentiel() {
		// TODO Auto-generated method stub
		DecodageSequentiel gsTaquin = new DecodageSequentiel(null);
		
		NoeudTaquin debutTaquin = new NoeudTaquin("", sequenceRepresentee);
		debutTaquin.construireNom();
		
		int[] dernier = finTaquin();
		NoeudTaquin dernierNoeud = new NoeudTaquin("fin", dernier);
		dernierNoeud.construireNom();
		
		ArrayList<Noeud> finTaquin = new ArrayList<Noeud>();
		finTaquin.add(dernierNoeud);
		
		Integer resultTaquin = gsTaquin.algo(debutTaquin, finTaquin);
		
		System.out.println(gsTaquin.resultat().toString());

		System.out.println("Result algo : "+resultTaquin.toString());
		Simulateur simuler = new Simulateur(gsTaquin.resultat());
		simuler.start();
		
	}

	private int[] finTaquin() {
		// TODO Auto-generated method stub
		int[] sequenceFinale = new int[dimension*dimension];
		for(int i = 0; i<sequenceFinale.length; i++){
			sequenceFinale[i] = i+1;
		}
		return sequenceFinale;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		System.out.println("Lancement de l'Application");
		Application.getInstance();
		
				
		// EXEMPLE DE VALIDATION
		/*
		HashMap<Noeud, HashMap<Noeud, Integer>> successeurs = new HashMap<Noeud, HashMap<Noeud,Integer>>();
		Noeud S = new Noeud("S");
		Noeud A = new Noeud("A");
		Noeud B = new Noeud("B");
		Noeud C = new Noeud("C");
		Noeud D = new Noeud("D");
		Noeud E = new Noeud("E");
		Noeud F = new Noeud("F");
		Noeud G = new Noeud("G");
		
		HashMap<Noeud, Integer> tmp1 = new HashMap<Noeud, Integer>();
		tmp1.put(A, 3);
		tmp1.put(B, 6);
		successeurs.put(S, tmp1);
		
		HashMap<Noeud, Integer> tmp2 = new HashMap<Noeud, Integer>();
		tmp2.put(S, 3);
		tmp2.put(B, 2);
		tmp2.put(C, 6);
		successeurs.put(A, tmp2);
		
		HashMap<Noeud, Integer> tmp3 = new HashMap<Noeud, Integer>();
		tmp3.put(S, 6);
		tmp3.put(A, 2);
		tmp3.put(D, 7);
		successeurs.put(B, tmp3);
		
		HashMap<Noeud, Integer> tmp4 = new HashMap<Noeud, Integer>();
		tmp4.put(D, 4);
		tmp4.put(E, 3);
		successeurs.put(C, tmp4);
		
		HashMap<Noeud, Integer> tmp5 = new HashMap<Noeud, Integer>();
		tmp5.put(B, 7);
		tmp5.put(F, 4);
		successeurs.put(D, tmp5);
		
		HashMap<Noeud, Integer> tmp6 = new HashMap<Noeud, Integer>();
		tmp6.put(G, 10);
		successeurs.put(E, tmp6);
		
		GraphSearch gs = new DecodageSequentiel(successeurs);
		ArrayList<Noeud> fin = new ArrayList<Noeud>();
		fin.add(F);
		gs.algo(debut, fin);
		*/
		/*
		HashMap<Noeud, HashMap<Noeud, Integer>> successeurs = new HashMap<Noeud, HashMap<Noeud,Integer>>();
		Noeud A = new Noeud("A");
		Noeud B = new Noeud("B");
		Noeud C = new Noeud("C");
		Noeud Z = new Noeud("Z");
		
		HashMap<Noeud, Integer> tmp1 = new HashMap<Noeud, Integer>();
		tmp1.put(B, 5);
		tmp1.put(C, 12);
		tmp1.put(Z, 18);
		successeurs.put(A, tmp1);
		
		HashMap<Noeud, Integer> tmp2 = new HashMap<Noeud, Integer>();
		tmp2.put(C, 6);
		tmp2.put(Z, 10);
		successeurs.put(B, tmp2);
		
		HashMap<Noeud, Integer> tmp3 = new HashMap<Noeud, Integer>();
		tmp3.put(Z, 3);
		successeurs.put(C, tmp3);
				
		DecodageSequentiel gs = new DecodageSequentiel(successeurs);
		ArrayList<Noeud> fin = new ArrayList<Noeud>();
		fin.add(Z);
		
		Integer result = gs.algo(A, fin);
		*/
		/*
		 * Taquin test
		 */
		/*
		GraphSearch gsTaquin = new DecodageSequentiel(null);
		
		int[] d = {4, 1, 3, 2};
		NoeudTaquin debutTaquin = new NoeudTaquin("", d);
		debutTaquin.construireNom();
		
		int[] dernier = {1, 2, 3, 4};
		NoeudTaquin dernierNoeud = new NoeudTaquin("fin", dernier);
		dernierNoeud.construireNom();
		
		ArrayList<Noeud> finTaquin = new ArrayList<Noeud>();
		finTaquin.add(dernierNoeud);
		
		Integer resultTaquin = gsTaquin.algo(debutTaquin, finTaquin);
		
		System.out.println(gsTaquin.resultat().toString());

		System.out.println("Result algo : "+resultTaquin.toString());
		*/
	}

}
